package cn.song.controller;

import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * 请求转发控制器
 *
 * @author SongRenShuo
 * @date 2024/05/20
 */
@Slf4j
@RestController
public class ApiController {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 转发请求至指定URL
     *
     * @param request    当前请求对象，用于获取转发目标URL
     * @param headers    要转发的HTTP头信息
     * @param formParams 转发时携带的表单参数
     * @param body       POST或PUT请求时的请求体
     * @return 转发操作的结果响应
     */
    @RequestMapping(value = "/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.PATCH, RequestMethod.OPTIONS, RequestMethod.HEAD, RequestMethod.TRACE})
    public ResponseEntity<byte[]> forwardRequestSamePath(HttpServletRequest request,
                                                         @RequestHeader HttpHeaders headers,
                                                         @RequestParam MultiValueMap<String, String> formParams,
                                                         @RequestBody(required = false) String body) {
        // 获取请求的URI路径
        String basePath = request.getRequestURI();
        // 获取上下文路径长度，用于去除basePath中的contextPath部分
        int contextPathLength = request.getContextPath().length();
        // 假设目标基础URL作为参数传递
        String targetBaseUrl = request.getParameter("targetBaseUrl");
        if (targetBaseUrl == null) {
            return ResponseEntity.badRequest().body("缺少 'targetBaseUrl' 参数".getBytes(StandardCharsets.UTF_8));
        }

        // 构建完整的目标URL，移除basePath中的contextPath部分并拼接targetBaseUrl
        String targetUrl = targetBaseUrl + basePath.substring(contextPathLength);
        formParams.remove("targetBaseUrl");

        UUID uuid = UUID.fastUUID();

        HttpMethod method = HttpMethod.resolve(request.getMethod());
        if (method == null) {
            log.error("请求转发失败,uuid:{}, 目标URL: {}, 原因: {}", uuid, targetUrl, "无效的HTTP请求类型：" + request.getMethod());
            return ResponseEntity.badRequest().body(("无效的HTTP请求类型：" + request.getMethod()).getBytes(StandardCharsets.UTF_8));
        }

        // 获取请求实体（处理文件上传和普通表单参数）
        HttpEntity<?> requestEntity = getRequestEntity(uuid, request, headers, formParams, body, targetUrl);

        if (!formParams.isEmpty()) {
            // 将表单参数附加到目标URL上
            targetUrl = UriComponentsBuilder.fromUriString(targetUrl).queryParams(formParams).toUriString();
        }

        log.info("请求转发开始,uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, \r\n头部信息: {}", uuid, targetUrl, method, body, formParams, headers);

        try {
            // 设置请求和响应的编码为utf-8
            restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
            // 设置请求头中的Accept-Charset为utf-8
            headers.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));

            // 使用RestTemplate发送请求并获取响应
            ResponseEntity<byte[]> responseEntity = restTemplate.exchange(targetUrl, method, requestEntity, byte[].class);

            // 获取响应头中的Content-Type
            String contentType = responseEntity.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
            Charset charset = getCharsetFromContentType(contentType);

            // 获取响应体
            byte[] responseBodyBytes = responseEntity.getBody();

            // 根据Content-Type处理响应内容
            if (charset != null && contentType.toLowerCase().contains("text") && responseBodyBytes != null) {
                String responseBody = new String(responseBodyBytes, charset);
                log.info("请求转发结束,uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, \r\n头部信息: {}, \r\n响应: {}",
                        uuid, targetUrl, method, body, formParams, headers, responseBody);
                return ResponseEntity.status(responseEntity.getStatusCode())
                        .headers(responseEntity.getHeaders())
                        .body(responseBody.getBytes(charset));
            } else if (responseBodyBytes != null) {
                String binaryDataPreview = new String(responseBodyBytes, StandardCharsets.UTF_8);
                log.info("请求转发结束,uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, \r\n头部信息: {}, \r\n响应: 二进制数据预览 ({}) 字节: {}",
                        uuid, targetUrl, method, body, formParams, headers, responseBodyBytes.length, binaryDataPreview);
                return ResponseEntity.status(responseEntity.getStatusCode())
                        .headers(responseEntity.getHeaders())
                        .body(responseBodyBytes);
            } else {
                log.info("请求转发结束,uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, \r\n头部信息: {}, \r\n响应: 空响应体",
                        uuid, targetUrl, method, body, formParams, headers);
                return ResponseEntity.status(responseEntity.getStatusCode())
                        .headers(responseEntity.getHeaders())
                        .body(new byte[0]);
            }
        } catch (HttpClientErrorException ex) {
            log.error("请求转发失败(响应非200),uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, 原因: {}",
                    uuid, targetUrl, method, body, formParams, ex.getMessage(), ex);
            HttpHeaders errorHeaders = ex.getResponseHeaders() != null ? ex.getResponseHeaders() : new HttpHeaders();
            byte[] responseBodyBytes = ex.getResponseBodyAsByteArray();
            if (responseBodyBytes.length > 0) {
                String errorMessage = new String(responseBodyBytes, StandardCharsets.UTF_8);
                log.error("请求转发失败(响应非200),uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, 原因: {}, 错误响应体: {}",
                        uuid, targetUrl, method, body, formParams, ex.getMessage(), errorMessage);
            }
            return ResponseEntity.status(ex.getStatusCode())
                    .headers(errorHeaders)
                    .body(getErrorResponseBody(responseBodyBytes, errorHeaders));
        } catch (Exception e) {
            log.error("请求转发失败,uuid:{}, 目标URL: {}, 方法: {}, 请求数据body: {},请求数据formParams: {}, 原因: {}",
                    uuid, targetUrl, method, body, formParams, e.getMessage(), e);
            return ResponseEntity.status(500)
                    .body(("请求转发时发生错误:" + e.getMessage()).getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * 获取请求实体
     *
     * @param uuid       唯一标识符，用于日志记录
     * @param request    HTTP请求对象
     * @param headers    请求头信息
     * @param formParams 表单参数
     * @param body       请求体内容
     * @param targetUrl  目标URL
     * @return 包含请求体和请求头的HttpEntity对象
     */
    private HttpEntity<?> getRequestEntity(UUID uuid, HttpServletRequest request, HttpHeaders headers, MultiValueMap<String, String> formParams, String body, String targetUrl) {
        // 检查请求是否为多部分请求（包含文件上传）
        if (request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            MultiValueMap<String, Object> multipartBody = new LinkedMultiValueMap<>();

            // 添加所有文件到multipartBody
            for (Iterator<String> it = multipartRequest.getFileNames(); it.hasNext(); ) {
                String fileName = it.next();
                List<MultipartFile> files = multipartRequest.getFiles(fileName);
                for (MultipartFile file : files) {
                    try {
                        multipartBody.add(fileName, new ByteArrayResource(file.getBytes()) {
                            @Override
                            public String getFilename() {
                                return file.getOriginalFilename();
                            }
                        });
                    } catch (IOException e) {
                        log.error("文件读取失败,uuid:{}, 目标URL: {}, 原因: {}", uuid, targetUrl, e.getMessage(), e);
                        return ResponseEntity.badRequest().body("文件读取失败:" + Arrays.toString(e.getMessage().getBytes(StandardCharsets.UTF_8)));
                    }
                }
            }

            // 添加所有表单参数到multipartBody
            for (String key : formParams.keySet()) {
                List<String> values = formParams.get(key);
                for (String value : values) {
                    multipartBody.add(key, value);
                }
            }
            return new HttpEntity<>(multipartBody, headers);
        } else {
            // 如果不是多部分请求，则直接使用请求体和请求头构建HttpEntity
            return new HttpEntity<>(body, headers);
        }
    }

    /**
     * 从Content-Type头中提取字符集
     *
     * @param contentType Content-Type头信息
     * @return 字符集，如果没有找到则返回null
     */
    private Charset getCharsetFromContentType(String contentType) {
        if (contentType == null) {
            return null;
        }
        String[] parts = contentType.split(";");
        for (String part : parts) {
            part = part.trim();
            if (part.startsWith("charset=")) {
                String charsetName = part.substring("charset=".length());
                try {
                    return Charset.forName(charsetName);
                } catch (IllegalArgumentException e) {
                    log.error("无效的字符集: {}", charsetName);
                    return null;
                }
            }
        }
        return null;
    }

    /**
     * 处理错误响应体
     *
     * @param responseBody 错误响应体字节数组
     * @param headers      错误响应头
     * @return 处理后的错误响应体字节数组
     */
    private byte[] getErrorResponseBody(byte[] responseBody, HttpHeaders headers) {
        String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);
        Charset charset = getCharsetFromContentType(contentType);
        if (charset != null && contentType.toLowerCase().contains("text")) {
            return new String(responseBody, charset).getBytes(charset);
        } else {
            return responseBody;
        }
    }
}



